﻿Option Strict On

Imports System.Security.Cryptography

Namespace FTP.Hashing

    Friend Class Crc32
        Inherits HashAlgorithm
        ''' <summary>
        ''' 
        ''' </summary>
        Public Const DefaultPolynomial As UInt32 = &HEDB88320UI
        ''' <summary>
        ''' 
        ''' </summary>
        Public Const DefaultSeed As UInt32 = &HFFFFFFFFUI

        Private _Hash As UInt32
        Private _Seed As UInt32
        Private _Table As UInt32()
        Private Shared defaultTable As UInt32()

        ''' <summary>
        ''' 
        ''' </summary>
        Public Sub New()
            MyClass.New(DefaultPolynomial, DefaultSeed)
        End Sub

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="polynomial"></param>
        ''' <param name="seed"></param>
        Public Sub New(ByVal polynomial As UInt32, ByVal seed As UInt32)
            MyBase.New()
            Me._Seed = seed
            Initialize()
            Me._Table = InitializeTable(polynomial)
        End Sub

        Public Overrides Sub Initialize()
            Me._Hash = Me._Seed
        End Sub

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="buffer"></param>
        ''' <param name="start"></param>
        ''' <param name="length"></param>
        Protected Overrides Sub HashCore(ByVal buffer As Byte(), ByVal start As Integer, ByVal length As Integer)
            _Hash = CalculateHash(_Table, _Hash, buffer, start, length)
        End Sub

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <return></return>
        Protected Overrides Function HashFinal() As Byte()
            Dim hashBuffer As Byte() = UInt32ToBigEndianBytes(Not _Hash)
            Me.HashValue = hashBuffer
            Return hashBuffer
        End Function

        ''' <summary>
        ''' 
        ''' </summary>
        Public Overrides ReadOnly Property HashSize() As Integer
            Get
                Return 32
            End Get
        End Property

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="buffer"></param>
        ''' <return></return>
        Public Shared Function Compute(ByVal buffer As Byte()) As UInt32
            Return Not CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length)
        End Function

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="seed"></param>
        ''' <param name="buffer"></param>
        ''' <return></return>
        Public Shared Function Compute(ByVal seed As UInt32, ByVal buffer As Byte()) As UInt32
            Return Not CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length)
        End Function

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="polynomial"></param>
        ''' <param name="seed"></param>
        ''' <param name="buffer"></param>
        ''' <return></return>
        Public Shared Function Compute(ByVal polynomial As UInt32, ByVal seed As UInt32, ByVal buffer As Byte()) As UInt32
            Return Not CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length)
        End Function

        Private Shared Function InitializeTable(ByVal polynomial As UInt32) As UInt32()
            If polynomial = DefaultPolynomial AndAlso defaultTable IsNot Nothing Then
                Return defaultTable
            End If

            Dim createTable As UInt32() = New UInt32(255) {}
            For i As Integer = 0 To 255
                Dim entry As UInt32 = CType(i, UInt32)
                For j As Integer = 0 To 7
                    If (entry And 1).Equals(1) Then
                        entry = (entry >> 1) Xor polynomial
                    Else
                        entry = entry >> 1
                    End If
                Next
                createTable(i) = entry
            Next

            If polynomial = DefaultPolynomial Then
                defaultTable = createTable
            End If

            Return createTable
        End Function

        Private Shared Function CalculateHash(ByVal table As UInt32(), ByVal seed As UInt32, ByVal buffer As Byte(), ByVal start As Integer, ByVal size As Integer) As UInt32
            Dim crc As UInt32 = seed
            For i As Integer = start To size - 1
                crc = (crc >> 8) Xor table(CInt(buffer(i) Xor crc And &HFF))

            Next
            Return crc
        End Function

        Private Function UInt32ToBigEndianBytes(ByVal x As UInt32) As Byte()
            Return New Byte() {CByte((x >> 24) And &HFF), CByte((x >> 16) And &HFF), CByte((x >> 8) And &HFF), CByte(x And &HFF)}
        End Function
    End Class
End Namespace